package record.core;

import org.springframework.util.Assert;
import record.annotation.RecordAnnotation;
import record.consts.OperateType;
import record.treeResult.Item;
import record.type.SimpleTypeRegistry;
import record.type.TypeHandler;

import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Package: com.record.core
 * @ClassName: RecordCreateService
 * @Author: wulongkai
 * @CreateTime: 2023/12/19 15:41
 * @Description:
 */
public class RecordCreateService {

    // 新增
    public static Item addRecord(Object bean) {
        return createRecordByOperateType(bean, OperateType.ADD);
    }

    // 删除
    public static Item delRecord(Object bean) {
        return createRecordByOperateType(bean, OperateType.DELETE);
    }

    // 更新
    public static Item updateRecord(Object oldBean, Object newBean) {
        return getUpdateRecord(oldBean, newBean);
    }

    public static Item createPublishRecord(Object oldBean, Object newBean) {
        if (oldBean == null) {
            return addRecord(newBean);
        } else if (newBean == null) {
            return delRecord(oldBean);
        } else {
            return updateRecord(oldBean, newBean);
        }
    }

    private static Item createRecordByOperateType(Object bean, OperateType operateType) {
        Item parentItem = new Item();
        try {
            Class<?> entityClass = bean.getClass();
            RecordAnnotation entityAnnotation = entityClass.getAnnotation(RecordAnnotation.class);
            if (entityAnnotation != null) {
                parentItem.setDesc(entityAnnotation.desc());
                parentItem.setName(entityClass.getSimpleName());
                List<Item> subItemList = new ArrayList<>();
                parentItem.setSubItem(subItemList);

                Field[] fields = entityClass.getDeclaredFields();
                for (Field field : fields) {
                    field.setAccessible(true);
                    RecordAnnotation fieldAnnotation = field.getAnnotation(RecordAnnotation.class);
                    if (fieldAnnotation != null) {
                        String desc = fieldAnnotation.desc();
                        Object fieldValue = field.get(bean);

                        if (SimpleTypeRegistry.isSimpleType(field.getType())) { // 简单类型
                            Class<?> handlerClass = fieldAnnotation.typeHandlerClass();
                            TypeHandler typeHandler = SimpleTypeRegistry.getTypeHandler(handlerClass);
                            fieldValue = typeHandler.handler(fieldValue);

                            Item subItem = new Item();
                            subItem.setDesc(desc);
                            subItem.setName(field.getName());
                            if (operateType == OperateType.ADD) {
                                subItem.setOperateType(OperateType.ADD);
                                subItem.setAfterValue(fieldValue);
                            } else if (operateType == OperateType.DELETE) {
                                subItem.setOperateType(OperateType.DELETE);
                                subItem.setBeforeValue(fieldValue);
                            }
                            subItemList.add(subItem);

                        } else if (Collection.class.isAssignableFrom(field.getType())) { // Collection
                            Field mapKeyField = getMapKeyField(fieldValue);
                            Map<?, ?> fieldValueMap = ((Collection<?>) fieldValue).stream().collect(
                                    Collectors.toMap(obj -> getMapKey(obj, mapKeyField), obj -> obj)
                            );
                            Item subItem = handleCollection(fieldValueMap, desc, field.getName(), operateType);
                            subItemList.add(subItem);
                        } else if (Map.class.isAssignableFrom(field.getType())) {
                            Item subItem = handleCollection((Map<?, ?>) fieldValue, desc, field.getName(), operateType);
                            subItemList.add(subItem);
                        } else { // 普通对象
                            Item subItem = createRecordByOperateType(fieldValue, operateType);
                            subItemList.add(subItem);
                        }
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return parentItem;
    }

    private static Item getUpdateRecord(Object oldBean, Object newBean) {
        Item parentItem = new Item();
        try {
            Class<?> oldBeanClass = oldBean.getClass();
            Class<?> newBeanClass = newBean.getClass();
            Assert.isTrue(oldBeanClass.getName().equals(newBeanClass.getName()), "对象类型不一致, oldBean: " + oldBeanClass.getName() + " newBean: " + newBeanClass.getName());

            Class<?> entityClass = oldBean.getClass();
            RecordAnnotation entityAnnotation = entityClass.getAnnotation(RecordAnnotation.class);
            if (entityAnnotation != null) {
                parentItem.setDesc(entityAnnotation.desc());
                parentItem.setName(entityClass.getSimpleName());
                List<Item> subItemList = new ArrayList<>();
                parentItem.setSubItem(subItemList);

                Field[] fields = entityClass.getDeclaredFields();
                for (Field field : fields) {
                    field.setAccessible(true);
                    RecordAnnotation fieldAnnotation = field.getAnnotation(RecordAnnotation.class);
                    if (fieldAnnotation != null) {
                        String desc = fieldAnnotation.desc();
                        Object oldFieldValue = field.get(oldBean);
                        Object newFieldValue = field.get(newBean);

                        if (SimpleTypeRegistry.isSimpleType(field.getType())) { // 简单类型
                            if (!Objects.equals(oldFieldValue, newFieldValue)) {
                                Class<?> handlerClass = fieldAnnotation.typeHandlerClass();
                                TypeHandler typeHandler = SimpleTypeRegistry.getTypeHandler(handlerClass);
                                oldFieldValue = typeHandler.handler(oldFieldValue);
                                newFieldValue = typeHandler.handler(newFieldValue);

                                Item subItem = new Item();
                                subItem.setDesc(desc);
                                subItem.setName(field.getName());
                                subItem.setOperateType(OperateType.UPDATE);
                                subItem.setBeforeValue(oldFieldValue);
                                subItem.setAfterValue(newFieldValue);
                                subItemList.add(subItem);
                            }

                        } else if (Collection.class.isAssignableFrom(field.getType())) { // Collection
                            Field mapKeyField = getMapKeyField(oldFieldValue);
                            Map<?, ?> oldFieldValueMap = ((Collection<?>) oldFieldValue).stream().collect(
                                    Collectors.toMap(obj -> getMapKey(obj, mapKeyField), obj -> obj)
                            );
                            Map<?, ?> newFieldValueMap = ((Collection<?>) newFieldValue).stream().collect(
                                    Collectors.toMap(obj -> getMapKey(obj, mapKeyField), obj -> obj)
                            );
                            Item subItem = handleCollection(oldFieldValueMap, newFieldValueMap, desc, field.getName());
                            subItemList.add(subItem);
                        } else if (Map.class.isAssignableFrom(field.getType())) {
                            Item subItem = handleCollection((Map<?, ?>) oldFieldValue, (Map<?, ?>) newFieldValue, desc, field.getName());
                            subItemList.add(subItem);
                        } else { // 普通对象
                            Item subItem = createPublishRecord(oldFieldValue, newFieldValue);
                            subItemList.add(subItem);
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return parentItem;
    }

    private static Item handleCollection(Map<?, ?> fieldValueMap, String itemDesc, String itemName, OperateType operateType) {
        List<Item> subItemList = new ArrayList<>();
        fieldValueMap.values().forEach(o -> {
            Item subItem = createRecordByOperateType(o, operateType);
            subItemList.add(subItem);
        });
        Item parentItem = new Item();
        parentItem.setDesc(itemDesc);
        parentItem.setName(itemName);
        parentItem.setSubItem(subItemList);
        return parentItem;
    }

    private static Item handleCollection(Map<?, ?> oldFieldValueMap, Map<?, ?> newFieldValueMap, String itemDesc, String itemName) {
        List<Item> subItemList = new ArrayList<>();
        oldFieldValueMap.values().forEach(o -> {
            if (!newFieldValueMap.containsKey(o)) { // 删除
                Item subItem = createPublishRecord(o, null);
                subItemList.add(subItem);
            } else { // 更新
                Item subItem = createPublishRecord(o, newFieldValueMap.get(o));
                subItemList.add(subItem);
            }
        });
        newFieldValueMap.values().forEach(o -> {
            if (!oldFieldValueMap.containsKey(o)) { // 新增
                Item subItem = createPublishRecord(null, o);
                subItemList.add(subItem);
            }
        });
        Item parentItem = new Item();
        parentItem.setDesc(itemDesc);
        parentItem.setName(itemName);
        parentItem.setSubItem(subItemList);
        return parentItem;
    }

    private static Field getMapKeyField(Object bean) {
        Field mapKeyField = null;
        Field[] fields = bean.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            RecordAnnotation fieldAnnotation = field.getAnnotation(RecordAnnotation.class);
            if (fieldAnnotation != null && fieldAnnotation.mapKey()) {
                mapKeyField = field;
                break;
            }
        }
        return mapKeyField;
    }

    private static Object getMapKey(Object obj, Field mapKeyField) {
        try {
            if (mapKeyField == null) {
                return obj;
            }
            return mapKeyField.get(obj);
        } catch (Exception e) {
            throw new RuntimeException("Error accessing field " + mapKeyField.getName(), e);
        }
    }
}
